// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "gpu/command_buffer/service/vertex_attrib_manager.h"

#include "base/memory/scoped_ptr.h"
#include "gpu/command_buffer/service/buffer_manager.h"
#include "gpu/command_buffer/service/error_state_mock.h"
#include "gpu/command_buffer/service/feature_info.h"
#include "gpu/command_buffer/service/gpu_service_test.h"
#include "gpu/command_buffer/service/test_helper.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gl/gl_mock.h"

using ::testing::_;
using ::testing::Pointee;

namespace gpu {
namespace gles2 {

    class VertexAttribManagerTest : public GpuServiceTest {
    public:
        static const uint32 kNumVertexAttribs = 8;

        VertexAttribManagerTest()
        {
        }

        ~VertexAttribManagerTest() override { }

    protected:
        void SetUp() override
        {
            GpuServiceTest::SetUp();

            for (uint32 ii = 0; ii < kNumVertexAttribs; ++ii) {
                EXPECT_CALL(*gl_, VertexAttrib4f(ii, 0.0f, 0.0f, 0.0f, 1.0f))
                    .Times(1)
                    .RetiresOnSaturation();
            }

            manager_ = new VertexAttribManager();
            manager_->Initialize(kNumVertexAttribs, true);
        }

        scoped_refptr<VertexAttribManager> manager_;
    };

// GCC requires these declarations, but MSVC requires they not be present
#ifndef COMPILER_MSVC
    const uint32 VertexAttribManagerTest::kNumVertexAttribs;
#endif

    TEST_F(VertexAttribManagerTest, Basic)
    {
        EXPECT_TRUE(manager_->GetVertexAttrib(kNumVertexAttribs) == NULL);
        EXPECT_FALSE(manager_->HaveFixedAttribs());

        const VertexAttribManager::VertexAttribList& enabled_attribs = manager_->GetEnabledVertexAttribs();
        EXPECT_EQ(0u, enabled_attribs.size());

        for (uint32 ii = 0; ii < kNumVertexAttribs; ii += kNumVertexAttribs - 1) {
            VertexAttrib* attrib = manager_->GetVertexAttrib(ii);
            ASSERT_TRUE(attrib != NULL);
            EXPECT_EQ(ii, attrib->index());
            EXPECT_TRUE(attrib->buffer() == NULL);
            EXPECT_EQ(0, attrib->offset());
            EXPECT_EQ(4, attrib->size());
            EXPECT_EQ(static_cast<GLenum>(GL_FLOAT), attrib->type());
            EXPECT_EQ(GL_FALSE, attrib->normalized());
            EXPECT_EQ(0, attrib->gl_stride());
            EXPECT_FALSE(attrib->enabled());
            manager_->Enable(ii, true);
            EXPECT_TRUE(attrib->enabled());
        }
    }

    TEST_F(VertexAttribManagerTest, Enable)
    {
        const VertexAttribManager::VertexAttribList& enabled_attribs = manager_->GetEnabledVertexAttribs();

        VertexAttrib* attrib1 = manager_->GetVertexAttrib(1);
        VertexAttrib* attrib2 = manager_->GetVertexAttrib(3);

        manager_->Enable(1, true);
        ASSERT_EQ(1u, enabled_attribs.size());
        EXPECT_TRUE(attrib1->enabled());
        manager_->Enable(3, true);
        ASSERT_EQ(2u, enabled_attribs.size());
        EXPECT_TRUE(attrib2->enabled());

        manager_->Enable(1, false);
        ASSERT_EQ(1u, enabled_attribs.size());
        EXPECT_FALSE(attrib1->enabled());

        manager_->Enable(3, false);
        ASSERT_EQ(0u, enabled_attribs.size());
        EXPECT_FALSE(attrib2->enabled());
    }

    TEST_F(VertexAttribManagerTest, SetAttribInfo)
    {
        BufferManager buffer_manager(NULL, NULL);
        buffer_manager.CreateBuffer(1, 2);
        Buffer* buffer = buffer_manager.GetBuffer(1);
        ASSERT_TRUE(buffer != NULL);

        VertexAttrib* attrib = manager_->GetVertexAttrib(1);

        manager_->SetAttribInfo(1, buffer, 3, GL_SHORT, GL_TRUE, 32, 32, 4, GL_TRUE);

        EXPECT_EQ(buffer, attrib->buffer());
        EXPECT_EQ(4, attrib->offset());
        EXPECT_EQ(3, attrib->size());
        EXPECT_EQ(static_cast<GLenum>(GL_SHORT), attrib->type());
        EXPECT_EQ(GL_TRUE, attrib->normalized());
        EXPECT_EQ(32, attrib->gl_stride());
        EXPECT_EQ(GL_TRUE, attrib->integer());

        // The VertexAttribManager must be destroyed before the BufferManager
        // so it releases its buffers.
        manager_ = NULL;
        buffer_manager.Destroy(false);
    }

    TEST_F(VertexAttribManagerTest, HaveFixedAttribs)
    {
        EXPECT_FALSE(manager_->HaveFixedAttribs());
        manager_->SetAttribInfo(1, NULL, 4, GL_FIXED, GL_FALSE, 0, 16, 0, GL_FALSE);
        EXPECT_TRUE(manager_->HaveFixedAttribs());
        manager_->SetAttribInfo(3, NULL, 4, GL_FIXED, GL_FALSE, 0, 16, 0, GL_FALSE);
        EXPECT_TRUE(manager_->HaveFixedAttribs());
        manager_->SetAttribInfo(1, NULL, 4, GL_FLOAT, GL_FALSE, 0, 16, 0, GL_FALSE);
        EXPECT_TRUE(manager_->HaveFixedAttribs());
        manager_->SetAttribInfo(3, NULL, 4, GL_FLOAT, GL_FALSE, 0, 16, 0, GL_FALSE);
        EXPECT_FALSE(manager_->HaveFixedAttribs());
    }

    TEST_F(VertexAttribManagerTest, CanAccess)
    {
        const GLenum kTarget = GL_ARRAY_BUFFER;
        MockErrorState error_state;
        BufferManager buffer_manager(NULL, NULL);
        buffer_manager.CreateBuffer(1, 2);
        Buffer* buffer = buffer_manager.GetBuffer(1);
        ASSERT_TRUE(buffer != NULL);

        VertexAttrib* attrib = manager_->GetVertexAttrib(1);

        EXPECT_TRUE(attrib->CanAccess(0));
        manager_->Enable(1, true);
        EXPECT_FALSE(attrib->CanAccess(0));

        manager_->SetAttribInfo(1, buffer, 4, GL_FLOAT, GL_FALSE, 0, 16, 0, GL_FALSE);
        EXPECT_FALSE(attrib->CanAccess(0));

        EXPECT_TRUE(buffer_manager.SetTarget(buffer, kTarget));
        TestHelper::DoBufferData(
            gl_.get(), &error_state, &buffer_manager, buffer,
            kTarget, 15, GL_STATIC_DRAW, NULL, GL_NO_ERROR);

        EXPECT_FALSE(attrib->CanAccess(0));
        TestHelper::DoBufferData(
            gl_.get(), &error_state, &buffer_manager, buffer,
            kTarget, 16, GL_STATIC_DRAW, NULL, GL_NO_ERROR);
        EXPECT_TRUE(attrib->CanAccess(0));
        EXPECT_FALSE(attrib->CanAccess(1));

        manager_->SetAttribInfo(1, buffer, 4, GL_FLOAT, GL_FALSE, 0, 16, 1, GL_FALSE);
        EXPECT_FALSE(attrib->CanAccess(0));

        TestHelper::DoBufferData(
            gl_.get(), &error_state, &buffer_manager, buffer,
            kTarget, 32, GL_STATIC_DRAW, NULL, GL_NO_ERROR);
        EXPECT_TRUE(attrib->CanAccess(0));
        EXPECT_FALSE(attrib->CanAccess(1));
        manager_->SetAttribInfo(1, buffer, 4, GL_FLOAT, GL_FALSE, 0, 16, 0, GL_FALSE);
        EXPECT_TRUE(attrib->CanAccess(1));
        manager_->SetAttribInfo(1, buffer, 4, GL_FLOAT, GL_FALSE, 0, 20, 0, GL_FALSE);
        EXPECT_TRUE(attrib->CanAccess(0));
        EXPECT_FALSE(attrib->CanAccess(1));

        // The VertexAttribManager must be destroyed before the BufferManager
        // so it releases its buffers.
        manager_ = NULL;
        buffer_manager.Destroy(false);
    }

    TEST_F(VertexAttribManagerTest, Unbind)
    {
        BufferManager buffer_manager(NULL, NULL);
        buffer_manager.CreateBuffer(1, 2);
        buffer_manager.CreateBuffer(3, 4);
        Buffer* buffer1 = buffer_manager.GetBuffer(1);
        Buffer* buffer2 = buffer_manager.GetBuffer(3);
        ASSERT_TRUE(buffer1 != NULL);
        ASSERT_TRUE(buffer2 != NULL);

        VertexAttrib* attrib1 = manager_->GetVertexAttrib(1);
        VertexAttrib* attrib3 = manager_->GetVertexAttrib(3);

        // Attach to 2 buffers.
        manager_->SetAttribInfo(
            1, buffer1, 3, GL_SHORT, GL_TRUE, 32, 32, 4, GL_FALSE);
        manager_->SetAttribInfo(
            3, buffer1, 3, GL_SHORT, GL_TRUE, 32, 32, 4, GL_FALSE);
        // Check they were attached.
        EXPECT_EQ(buffer1, attrib1->buffer());
        EXPECT_EQ(buffer1, attrib3->buffer());
        // Unbind unattached buffer.
        manager_->Unbind(buffer2);
        // Should be no-op.
        EXPECT_EQ(buffer1, attrib1->buffer());
        EXPECT_EQ(buffer1, attrib3->buffer());
        // Unbind buffer.
        manager_->Unbind(buffer1);
        // Check they were detached
        EXPECT_TRUE(NULL == attrib1->buffer());
        EXPECT_TRUE(NULL == attrib3->buffer());

        // The VertexAttribManager must be destroyed before the BufferManager
        // so it releases its buffers.
        manager_ = NULL;
        buffer_manager.Destroy(false);
    }

    // TODO(gman): Test ValidateBindings
    // TODO(gman): Test ValidateBindings with client side arrays.

} // namespace gles2
} // namespace gpu
